/*
* 柯里化:又称部分求值，其含义是给函数分步传递参数，每次传递参数后部分应用参数，并返回一个更具体的函数接受剩下的参数，这中间可嵌套多层这样的接受部分参数函数，直至返回最后结果。
          因此柯里化的过程是逐步传参，逐步缩小函数的适用范围，逐步求解的过程。
特点:
  1.延迟计算 2.参数复用 3.动态创建函数
*/

/**
 * [curry description] 通用柯里化函数
 * @param  {Function} fn [description]
 * @return {[type]}      [description]
 * @author zhangguanggui 2018-07-31
 */
function curry(fn) {
  let _args = [].slice.call(arguments, 1);
  return function() {
    if (arguments.length === 0) {
      return fn.apply(null, _args);
    }
    // 如果想在有参数的时候 获取值 可以重写valueOf

    // 保存参数
    _args = [].concat.call(_args, [].slice.call(arguments));
    // 返回函数
    return arguments.callee;
  };
}

/**
 * @经典求和:最后调用不需要带(),通过改写toString方法
 * @author 张光桂
 * @date 2018-10-13
 */
function add() {
  const getSum = function(arr, initial = 0) {
    return arr.reduce((sum, num) => sum + num, initial);
  };
  let sum = getSum([].slice.call(arguments));
  let temp = function() {
    sum = getSum([].slice.call(arguments), sum);
    return temp;
  };
  temp.toString = temp.toValue = () => sum;
  return temp;
}

/*
 *
 *反柯里化:反柯里化的作用在与扩大函数的适用性，使本来作为特定对象所拥有的功能的函数可以被任意对象所用.
 ** 通俗的解释可以是 函数的借用，是函数能够接受处理其他对象，通过借用泛化、扩大了函数的使用范围
 */

function unCurry(fn) {
  return function() {
    var _args = [].slice.call(arguments, 1);
    return fn.apply(arguments[0], _args);
  };
}

// 测试
var add = function() {
  return [].reduce.call(
    arguments,
    function(sum, item) {
      return sum + item;
    },
    0
  );
};

add = curry(add, 10);
add(20, 40);
console.log(add(30, 100, 1)());
add(10);
console.log(add(9)());
